home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Development Platforms / Apple II / Essentials / rTutors / Detailed.Docs / Rez.103 < prev   
Encoding:
Text File  |  1990-08-20  |  17.3 KB  |  331 lines  |  [TEXT/pdos]

  1. Rez 103
  2. by: Tim Swihart
  3.  
  4. This time around, we'll look at Parts 2.5 and 3 of the resource tutorial.
  5. We're taking on two parts instead of the traditional one like Rez 101 and
  6. Rez 102 covered since both Part 2.5 and Part 3 are fairly short.  
  7.  
  8.  
  9. Part 2.5
  10. The quick summary of Part 2.5 is that it's simply Part 2 with an event loop 
  11. added.  If you're totally lost as to what an event loop is then you're going
  12. to have a VERY hard time writing desktop applications and really should get 
  13. your hands on "Programmer's Introduction to the Apple IIGS" from Addison-
  14. Wesley (ISBN #: 0-201-17745-5).  That book explains in full detail what is 
  15. required to create a desktop application for the IIGS.  Among the many 
  16. concepts it explains are event loops.  To refresh those of you who are 
  17. vaguely familiar with event loops, that's where the action is in a desktop 
  18. application!
  19.  
  20. Unlike the apps of many older CPU's, desktop applications allow the user to 
  21. be in control instead of the computer.  What's the difference?  All menu
  22. items are always handy instead of having to go into a menu mode and from
  23. there navigate through several submenus to get things done.  Desktop 
  24. applications therefore can't make a lot of assumptions that text-screen apps 
  25. generally do.
  26.  
  27. To cope with this difference, the application spends its spare time in an 
  28. event loop.  That's just a small loop of code that waits for things to happen 
  29. (such as mouse clicks, key presses, etc) and executes various pieces of code 
  30. to cope with each of the different types of events.
  31.  
  32. To keep things simple, Part 2.5 the event loop I added only handles three 
  33. types of events,  They are:  wInGoAway (meaning that the user clicked in 
  34. the close box of an open window), wInSpecial, and wInMenuBar (the user 
  35. clicked the mouse on the menu bar - we need to open the menus for the user 
  36. and let them pick something).  All other events are either ignored or handled 
  37. for us by TaskMaster.
  38.  
  39.  
  40. By Who?
  41. TaskMaster is an extension to the Window Manager in the IIGS Toolbox that 
  42. ALL desktop computers should have (but they don't!).  TaskMaster will 
  43. handle all of the "generic" events that it can (windows being moved, 
  44. windows being zoomed, scroll bars being clicked, update events, etc.).  
  45. Having TaskMaster around makes writing desktop applications a LOT easier.
  46.  
  47. TaskMaster is the big reason that my event loop only cares about a couple of 
  48. events and yet still fully supports NDA's, updates the window's contents 
  49. correctly, can cut/copy/paste, etc.  Without TaskMaster, you would have to 
  50. write the code to do all that yourself.  No doubt you'd have a bug or two or 
  51. would simply not have some of the functionality that your users expect.  So, 
  52. don't call "GetNextEvent" to find out what your app needs to do - call 
  53. "TaskMaster" instead and you'll have a MUCH better app as a result!
  54.  
  55. More details on TaskMaster can be found in "Programmer's Introduction to 
  56. the IIGS", and Volumes 2 and 3 of the IIGS Toolbox Reference manual.  All of 
  57. these books should be in your library if you're serious about writing desktop 
  58. software for the IIGS.  You should also read them - just having them in your 
  59. library doesn't do much good if you never read them...  <grin>
  60.  
  61.  
  62. How Did You Do It?
  63. In order to add the event loop to the application, I had to create several new 
  64. procedures.  The first one is named "do_main_event" and is called just after 
  65. we start up the tools.  This is where the application's "heart" is - every
  66. time the app finishes handling an event, it comes back to the event loop to
  67. see if there are other events waiting to be handled.
  68.  
  69. Inside of "do_main_event", we call TaskMaster to find out if there are any 
  70. events waiting.  TaskMaster requires two things as input - a mask that tells 
  71. it which events you want to know about (each bit controls a different event) 
  72. and an event record to put the detailed information about the event into.  
  73. TaskMaster also returns an event code.  We use the event code as part of a 
  74. switch statement ("case" statement if you're using Pascal) to determine what 
  75. to do with the task code.  We only get task codes for things that TaskMaster 
  76. couldn't handle.
  77.  
  78. If a user clicks in the close box of a window, we don't really care - this app 
  79. has no windows of its own yet, and TaskMaster will close NDA windows for 
  80. us.
  81.  
  82. We really only need to watch for the user clicking in the menu bar.  If he 
  83. does, we call "do_menu_events".  This is one of the new procedures added 
  84. since Part 2.  "do_menu_events" figures out which menu item the user 
  85. selected.  We use another switch (case for Pascal folks) statement to handle 
  86. all of the different menu items.  Since we're smart about how we program 
  87. and used constants to represent each menu item, it's very easy to see how 
  88. the switch statement for the menu items works - it simply transfers control 
  89. to a different subroutine for each menu items.
  90.  
  91. Most of the menu items actually result in nothing happening.  The only 
  92. "active" menu items in Part 2.5 are the About box (do_show_about gets 
  93. called) and Quit (do_quit_app gets called).  Part 3 adds a window to the app 
  94. and Part 3.2 adds an AlertWindow for the About box.  For now, selecting the 
  95. About box menu item will result in SysBeep being called.  When you're 
  96. creating an application for the first time, it's worth spending some extra time 
  97. being certain that your menu items all work correctly (to catch tiny errors in 
  98. your source).  The easiest way to do this is to have each items bring up a 
  99. dialog box saying what item you just picked.  It only takes a couple lines of 
  100. code and can catch some pretty silly errors that would slip by otherwise.
  101.  
  102. The call to TaskMaster is done inside a loop (hence the origin of the name 
  103. "event loop") and we stay in that loop until the user picks "Quit" from the 
  104. menu.  Picking "Quit" causes the procedure "do_quit_app" to be called.  
  105. Remember, "do_quit_app" is several levels deep inside the app.  We can't 
  106. just quit from here - the stack would be messed up, the tools have to be 
  107. shutdown, etc.  We need a graceful way to tell the event loop to quit running 
  108. so control will be returned to the main application where we've already seen 
  109. (in the earlier parts) that things are shutdown nicely.
  110.  
  111.  
  112. Remember Your Booleans!
  113. Think hard now - remember the boolean from Part 1 that we set during the 
  114. "do_init_rom" routine?  Remember that it was used to control whether or not 
  115. the app should be run?  We're going to use it again!  (in case you don't 
  116. remember, I'm referring to "gPunt") If you look closely at the start of the 
  117. event loop ("while(!gPunt)") you'll see that as long as gPunt is set to False, 
  118. the event loop will keep running and the app won't shut down.
  119.  
  120. So, all we really need to do in "do_quit_app" is change the value of gPunt to 
  121. indicate that it's time to shut things down.  That causes the event loop to 
  122. terminate and returns control to the main app - letting the tools be shut 
  123. down and then the app itself is done.
  124.  
  125.  
  126. Why "do_quit_app"?
  127. Seems a little wasteful to have a procedure that does nothing more than set 
  128. a boolean!  Why not just set it on the line where we called "do_quit_app" 
  129. instead?  Simple - what happens in a complete app if you've made changes 
  130. to the current document and you pick "Quit" from the menu?  The app 
  131. prompts you to save the changes!  Where would that come from?  Why not 
  132. from a routine like "do_quit_app" that's already set up to handle 
  133. EVERYTHING that's needed to prepare the app for being shut down.
  134.  
  135. What if the user is prompted for saving their changes and picks "cancel"?  
  136. How would "do_quit_app" handle that?  Simple - just leave the boolean 
  137. "gPunt" set to False and the event loop would NOT terminate.  Everything 
  138. would just keep running as though the user never picked "Quit" at all.  Nice, 
  139. eh?
  140.  
  141. Structuring your program carefully results in an elegant design and an easy 
  142. to extend application.  If we'd just set the boolean right there in the switch 
  143. statement, then we'd have to squeeze in a lot of extra code later when the 
  144. app needed to handle other things before quitting.  That would destroy the 
  145. readability of our source code!  The easier it is to read your source code, the 
  146. easier it is to debug it!
  147.  
  148.  
  149. Wrap-up 2.5:
  150. So, Part 2.5 is pretty simple.  Yet, adding the event loop turned our app into 
  151. one that supports NDA's, and "feels" like a desktop application.  Prior to Part 
  152. 2.5 the app just started up and shut down - never giving you a chance to do 
  153. anything other than watch.  TaskMaster handles all of the things necessary 
  154. to make NDA's work for us (so, NDA support is essentially free) and we 
  155. really only needed to implement ONE menu item at this stage (the "Quit" 
  156. item).  Nothing new in the way of resources was added to go from Part 2 to 
  157. Part 2.5.  Part 2.5 was named that because it's about halfway between Part 2 
  158. and Part 3.
  159.  
  160.  
  161. Part 3:
  162. This part adds a window with a couple of controls in it to the application.  
  163. Using the controls in the window is very simple since we're using 
  164. TaskMaster.  Someday, when you're bored and have a LOT of spare time, try 
  165. implement just up to Part 3 WITHOUT using TaskMaster.  It's possible, but 
  166. it's a LOT of effort!
  167.  
  168. The window is stored as a resource (naturally) and the controls in are also 
  169. resources.  We could make the controls automatically be installed in the 
  170. window when it's open, but I did it in a separate step for two reasons.  The 
  171. first one is to show you how to do it (using NewControl2) and the second 
  172. reason is to avoid a bug in the System Disk 5.0.2 Window Manager that's 
  173. documented in IIGS Tech Note #82 (the tech notes are available online, so I 
  174. won't repeat it here).
  175.  
  176. To make the window show up, we have to add a couple of new routines to 
  177. our application.  The first one is "do_make_window" and it's called from 
  178. within "main" so that we have a window in our application BEFORE the event 
  179. loop gets called.  We create the actual window by making ONE tool call -> 
  180. "NewWindow2".  NewWindow2 needs quite a bit of information in order to 
  181. do its stuff.  First off, it needs to if you want to use a custom title for
  182. the window (we want to use the one in the resource, so put a NIL here).  Next, 
  183. NewWindow2 needs to know if it should use a custom refCon or not - again, 
  184. we want the default, so put a NIL here.  
  185.  
  186. TaskMaster will call our window update routine IF we put a pointer to that 
  187. routine in the third field of the NewWindow2 call.  Always do this if at all 
  188. possible - it will make your windows behave much nicer than if you try to 
  189. manually manage all update events that come along.
  190.  
  191. The fourth item to pass NewWindow2 is a NIL since we do NOT want to use a 
  192. custom def proc for our window.  If you're not sure what a def proc is, that's 
  193. what draws the window itself (the frame, title, etc).  If you wanted to have a 
  194. round window, you would have to write a custom def proc and set this field 
  195. to point to the def proc.
  196.  
  197. Fifth, NewWindow2 needs to know what kind of reference it's going to get in 
  198. the sixth field.  References were described in Rez 102, so go back and re-
  199. read about them if you forgot what a reference is!  Since we're using 
  200. resources, we put the resource ID of our window template in the sixth field.
  201.  
  202. The final field of the NewWindow2 call tells the Window Manager what 
  203. template it should use to create the window.  There are two different 
  204. standard resource templates that can be used to create windows from 
  205. resources.  We're using the first one.  If you want to explore the other one, 
  206. go right ahead - it's documented in Apple IIGS Toolbox Reference manual, 
  207. volume 3.
  208.  
  209. When NewWindow2 is done, it will return a pointer to the newly created 
  210. GrafPort.  We need that point for the next couple of tool calls, but otherwise 
  211. we can toss it out (i.e.:  it doesn't have to be kept in a global variable).
  212. If we need it back, we can get it by using tool calls such as FrontWindow, 
  213. GetNextWindow, GetFirstWindow, etc.
  214.  
  215.  
  216. Watch Out For The Bug!
  217. The bug I mentioned earlier is simply that the GrafPort isn't set right in 
  218. certain cases before controls are installed in a window.  To avoid it, we 
  219. simply set the current port (using the tool call "SetPort") to the newly 
  220. created window's GrafPort (which NewWindow2 returned a couple of 
  221. paragraphs ago).
  222.  
  223. Now that the port is set right, we can add our controls to the window's 
  224. contents by calling "NewControl2".  NewControl2 loads controls from the 
  225. resource fork and attaches them to a given window.  If you want to put only 
  226. one or tons of controls in a window, NewControl2 is for you!
  227.  
  228.  
  229. Elaborate Please...
  230. NewControl2 wants the GrafPort of the window it's supposed to put controls 
  231. in, it wants to know what kind of reference it's going to get, and it wants
  232. the actual reference (resource ID of the control list in our case).
  233.  
  234. NewControl2 looks for a list of controls and installs all of them with ONE tool 
  235. call.  To keep our app simple, we're adding only two controls - a push button 
  236. (pretty common) and a TextEdit field (to show the power of TaskMaster).
  237.  
  238.  
  239. TaskMaster Again?
  240. Yep - TaskMaster makes it VERY easy to track controls in a window.  Without
  241. TaskMaster, you would get a mouseDown event (telling you the mouse was
  242. clicked).  You'd have to find out WHERE it was clicked (in the menu bar?  in
  243. the window? etc).  If it was clicked in the window's content area, then you'd
  244. have to call FindControl (to see if it was clicked over a control such as a
  245. push button, radio button, etc).  If it was clicked over a control, you have
  246. to call TrackControl to be sure the mouse is over the same control when it's
  247. released!  Then you have to do whatever action that controls calls for.  UGH!!!
  248.  
  249. With TaskMaster, you're told AFTER the mouse comes up over the control 
  250. that you need to do whatever action that control calls for.  No more figuring 
  251. out where the mouse went down at!  No more FindControl/TrackControl!  
  252. YAY!
  253.  
  254. In the case of the TextEdit field, Cut/Copy/Paste/Clear/Undo are all done by 
  255. TaskMaster!  Look closely at the do_menu_events routine and you'll see that 
  256. our application does NOTHING in response to the user picking Undo, Cut, 
  257. Copy, Paste, or Clear from the menus!  Sure saves a lot of code, eh?
  258.  
  259. Like I said, TaskMaster is your friend - learn to let it do what it does best 
  260. and spend your effort on the stuff that's unique to the application you're 
  261. writing.
  262.  
  263.  
  264. Drawing The Contents
  265. TaskMaster will call our window content drawing routine for us as soon as 
  266. there's some spare time.  This spare time is known as "null events" and it 
  267. occurs when the computer gets bored (because it's waiting on the user to do 
  268. something that generates an event).  Since all of our contents this time 
  269. around are controls, we can draw then with ONE tool call!
  270.  
  271. Before we call that one tool, we have to know what GrafPort to draw them in, 
  272. so we call GetPort to find that out.  See, tool call names are pretty easy to 
  273. anticipate - that makes desktop programming MUCH easier!
  274.  
  275. The one call that draws it all is "DrawControls".  We tell it what GrafPort to 
  276. draw in and it figures out what all the controls are, loads them from the 
  277. resource fork if needed, and draws them!  Nice, simple, compact, complete, 
  278. easy!  The way it should be.
  279.  
  280. Remember - the application itself doesn't call the content drawing procedure, 
  281. TaskMaster does!  So if you're digging through the source code and can't 
  282. figure out who's calling these routine, so you want to throw it out - think 
  283. again!
  284.  
  285.  
  286. Play With It!
  287. Run the application that results from compiling the source code to rTutor 
  288. Part 3.  Try dragging the window around - naturally it works, but there's no 
  289. code in our application to do that.  TaskMaster does the dragging for us 
  290. (without TaskMaster, we'd have to do it ourselves - UGH!)  Try cutting and 
  291. pasting from the TextEdit field in the window.  It works like you'd expect - 
  292. thanks to TaskMaster.  Try clicking on the push button - works like it should 
  293. thanks to TaskMaster.
  294.  
  295. If you haven't caught on by now (I know, some of the hints were pretty 
  296. subtle), then I'll be more explicit ->  Use TaskMaster whenever possible!  
  297. Your application will "feel" better to the user!  Your application will be able 
  298. to do more of the "little things" that users expect.  TaskMaster is part of the 
  299. System, so why waste disk space duplicating its effort within your 
  300. application?  Just use TaskMaster and all will be right with the world...
  301.  
  302.  
  303. Wrap Up Time:
  304. OK, enough reading - get to it!  Spend some time studying the source code.  
  305. Look up each of the tool calls that you're not familiar with (you'll need Apple 
  306. IIGS Toolbox Reference Volume 3 for the calls that deal with resources).  If 
  307. you're hopelessly lost, post a plea for help on either GEnie or America 
  308. Online's developer forums.  On GEnie, post in A2Pro.  On America Online, post 
  309. in ADV.  If you're on another network, find a way to get to where I am or 
  310. post there - I'm not the only one who can field questions on resources.  Lots 
  311. of folks can deal with it just fine (they're just too lazy to write detailed
  312. docs like these).  <grin>
  313.  
  314.  
  315. If you don't have the following manuals yet, go get them!  They're all 
  316. Addison-Wesley books, so your local bookstore (B. Dalton's, Walden's, etc) 
  317. should either have them or can order them if you bring them the ISBN 
  318. number:
  319.  
  320.  
  321. Book Title                                      ISBN Number
  322. -------------------------------------------------------------
  323. Programmer's Introduction to the Apple IIGS     0-201-17745-5
  324. Apple IIGS Toolbox Reference, Volume 3          0-201-55019-9
  325. Apple IIGS Toolbox Reference, Volume 2          0-201-17747-1
  326. Apple IIGS Toolbox Reference, Volume 1          0-201-17746-3
  327.  
  328.  
  329. Enjoy!
  330. Tim S.
  331.